package com.softwin.cryptocurrency.demo.spread;

import com.softwin.cryptocurrency.demo.security.JwtCallCredential;
import com.softwin.grpc.common.Common;
import com.softwin.marketdata.gateway.service.MarketDataManageGrpc;
import com.softwin.marketdata.gateway.service.MarketGateway;
import com.softwin.order.reporting.service.*;
import io.grpc.ConnectivityState;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by michael tsao on 2018/08/18.
 */

public class SpreadStrategy {

    private class ConsoleLogger {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        public void Debug(String msg) {
            this.Log("Debug", msg);
        }

        public void Info(String msg) {
            this.Log("Info", msg);
        }

        public void Warn(String msg) {
            this.Log("Warn", msg);
        }

        public void Error(String msg) {
            this.Log("Error", msg);
        }

        private void Log(String type, String msg) {
            if (type == "Debug") return;
            System.out.println(String.format("%s %s: %s", formatter.format(new Date()), type, msg));
        }
    }

    private class OrderMap<K, V> {
        private final HashMap<K, V> map;
        private final ReadWriteLock lock = new ReentrantReadWriteLock();
        private final Lock r = lock.readLock();
        private final Lock w = lock.writeLock();

        public OrderMap(HashMap<K, V> map) {
            this.map = map;
        }

        public V put(K key, V value) {
            w.lock();
            logger.Debug("获取写锁");
            try {
                return map.put(key, value);
            } finally {
                w.unlock();
                logger.Debug("释放写锁");
            }
        }

        public V get(K key) {
            r.lock();
            logger.Debug("获取读锁");
            try {
                return map.get(key);
            } finally {
                r.unlock();
                logger.Debug("释放读锁");
            }
        }

        public V remove(K key) {
            r.lock();
            logger.Debug("获取读锁");
            try {
                return map.remove(key);
            } finally {
                r.unlock();
                logger.Debug("释放读锁");
            }
        }

        public Set<Map.Entry<K, V>> entrySet() {
            return map.entrySet();
        }
    }


    @Data
    private class ExpectedOpenClosePrice {
        private String arbitrageName;
        private BigDecimal expectedBuyPrice;
        private BigDecimal expectedSellPrice;
        private BigDecimal expectedStopBuyPrice;
        private BigDecimal expectedStopSellPrice;

        @Override
        public String toString() {
            return getArbitrageName() + " : Expected: "
                    + getExpectedBuyPrice().setScale(5, RoundingMode.HALF_EVEN).toString() + "," + getExpectedSellPrice().setScale(5, RoundingMode.FLOOR).toString() + ","
                    + getExpectedStopBuyPrice().setScale(5, RoundingMode.HALF_EVEN).toString() + "," + getExpectedStopSellPrice().setScale(5, RoundingMode.FLOOR).toString();
        }
    }

    @Data
    @AllArgsConstructor
    private class BidAsk1 {
        private String exchange;
        private BigDecimal bid1Price;
        private BigDecimal bid1Quantity;
        private BigDecimal ask1Price;
        private BigDecimal ask1Quantity;
        private long timestamp;

        public boolean equals(Object obj) {
            if (obj instanceof BidAsk1) {
                BidAsk1 bidAsk1 = (BidAsk1) obj;
                return (exchange.equals(bidAsk1.exchange) && bid1Price.compareTo(bidAsk1.bid1Price) == 0
                        && bid1Quantity.compareTo(bidAsk1.bid1Quantity) == 0 && ask1Price.compareTo(bidAsk1.ask1Price) == 0
                        && ask1Quantity.compareTo(bidAsk1.ask1Quantity) == 0);
            }
            return super.equals(obj);
        }

        public BigDecimal getLastPrice() {
            if (bid1Price != null && ask1Price != null)
                return (bid1Price.add(ask1Price)).divide(new BigDecimal("2"));
            else
                return BigDecimal.ZERO;
        }

        public int hashCode() {
            return exchange.hashCode() + bid1Price.toString().hashCode() + ask1Price.toString().hashCode() +
                    bid1Quantity.toString().hashCode() + ask1Quantity.toString().hashCode();
        }
    }

    @Data
    private class Precision {
        private String exchange;
        private String baseCurrency;
        private String quoteCurrency;
        private Common.Symbol symbol;

        private BigDecimal pricePrecision;
        private BigDecimal amountPrecision;
        private BigDecimal minAmount;

        @Override
        public String toString() {
            return exchange + ",baseCurrency=" + baseCurrency + " : quoteCurrency : " + quoteCurrency + ","
                    + getPricePrecision().setScale(5, RoundingMode.FLOOR).toString() + "," + getAmountPrecision().setScale(5, RoundingMode.FLOOR).toString() + ","
                    + getMinAmount().setScale(5, RoundingMode.FLOOR).toString();
        }
    }

    /*
    | string     | pricePrecision  | 价格精度           |
    | string     | amountPrecision | 数量精度           |
    | Symbol     | symbol          | 交易对             |
    | string     | minAmount       | 最小成交量         |
    */

    @Data
    private class ArbitrageSymbol {
        private String ExchangeA;
        private String ExchangeB;
        private String baseCurrency;
        private String quoteCurrency;
        private BidAsk1 exchangABidAsk1;
        private BidAsk1 exchangBBidAsk1;
        private String arbitrageName;
        private Precision aSymbolPrecision;
        private Precision bSymbolPrecision;
        private long updateTime;


        private StrategyStatus status;
        private StrategyStatus previousStatus;


        public ArbitrageSymbol(String exchangea, String exchangeb, String baseCurrency, String quoteCurrency) {
            this.ExchangeA = exchangea;
            this.ExchangeB = exchangeb;
            this.baseCurrency = baseCurrency;
            this.quoteCurrency = quoteCurrency;
            this.status = StrategyStatus.NoN;
            this.previousStatus = StrategyStatus.NoN;

            this.updateTime = 0;
        }

        public String getArbitrageName() {
            arbitrageName = getExchangeA() + "-" + getExchangeB() + ":" + this.getBaseCurrency() + "/" + this.getQuoteCurrency();
            return arbitrageName;
        }

        public BigDecimal getArbitrageBidPrice() {
            if (exchangABidAsk1 != null && exchangBBidAsk1 != null) {
                return exchangABidAsk1.getBid1Price().subtract(exchangBBidAsk1.getAsk1Price());
            }
            return BigDecimal.ZERO;
        }

        public BigDecimal getArbitrageAskPrice() {
            if (exchangABidAsk1 != null && exchangBBidAsk1 != null) {
                return exchangABidAsk1.getAsk1Price().subtract(exchangBBidAsk1.getBid1Price());
            }

            return BigDecimal.ZERO;
        }


        public BigDecimal getArbitrageBidQuantity() {
            if (exchangABidAsk1 != null && exchangBBidAsk1 != null) {
                return exchangABidAsk1.getBid1Quantity().setScale(5, RoundingMode.HALF_EVEN).min(exchangBBidAsk1.getAsk1Quantity().setScale(5, RoundingMode.FLOOR));
            }
            return BigDecimal.ZERO;
        }

        public BigDecimal getArbitrageAskQuantity() {
            if (exchangABidAsk1 != null && exchangBBidAsk1 != null) {
                return exchangABidAsk1.getAsk1Quantity().setScale(5, RoundingMode.HALF_EVEN).min(exchangBBidAsk1.getBid1Quantity().setScale(5, RoundingMode.FLOOR));
            }
            return BigDecimal.ZERO;
        }


        public void update(String exchange, BidAsk1 bidAsk1) {
            if (ExchangeA.equals(exchange))
                exchangABidAsk1 = bidAsk1;
            else if (ExchangeB.equals(exchange))
                exchangBBidAsk1 = bidAsk1;
            if (exchangABidAsk1 != null && exchangBBidAsk1 != null) {
                if (exchangABidAsk1.getTimestamp() > updateTime) {
                    updateTime = exchangABidAsk1.getTimestamp();
                }
                if (exchangBBidAsk1.getTimestamp() > updateTime) {
                    updateTime = exchangBBidAsk1.getTimestamp();
                }
            }

        }

        @Override
        public String toString() {
            return getArbitrageName() + ",status=" + status + ",previousStatus=" + previousStatus + ",updateTime=" + updateTime
                    + " : bid,bidQ,ask,askQ : " +
                    getArbitrageBidPrice().setScale(5, RoundingMode.FLOOR).toString() + "," + getArbitrageBidQuantity().setScale(5, RoundingMode.FLOOR).toString() + ","
                    + getArbitrageAskPrice().setScale(5, RoundingMode.FLOOR).toString() + "," + getArbitrageAskQuantity().setScale(5, RoundingMode.FLOOR).toString() + ","

                    /*+ " ExchangeA: bid1,bidQ,ask1,askQ : "
                    + getBaseBidAsk1().bid1Price.setScale(5, RoundingMode.HALF_EVEN).toString() + "," + getBaseBidAsk1().getBid1Quantity().setScale(5, RoundingMode.HALF_EVEN).toString() + ","
                    + getBaseBidAsk1().getAsk1Price().setScale(5, RoundingMode.HALF_EVEN).toString() + "," + getBaseBidAsk1().getAsk1Quantity().setScale(5, RoundingMode.HALF_EVEN).toString()
                    + " ExchangeB: bid1,bidQ,ask1,askQ : "
                    + getQuoteBidAsk1().bid1Price.setScale(5, RoundingMode.HALF_EVEN).toString() + "," + getQuoteBidAsk1().getBid1Quantity().setScale(5, RoundingMode.HALF_EVEN).toString() + ","
                    + getQuoteBidAsk1().getAsk1Price().setScale(5, RoundingMode.HALF_EVEN).toString() + "," + getQuoteBidAsk1().getAsk1Quantity().setScale(5, RoundingMode.HALF_EVEN).toString()
                  */;
        }
    }

    @Data
    private class Position {

        private String positionKey;
        private String exchange;
        private String subaccount;
        private String type;

        private String currency;
        //private String quoteCurrency;
        private BigDecimal currencyFree;
        private BigDecimal currencyLocked;
        //private BigDecimal quoteCurrencyFree;
        //private BigDecimal quoteCurrencyLocked;

        public String getPositionKey() {
            if (exchange != null && currency != null)
                positionKey = exchange + currency;
            return positionKey;
        }

        @Override
        public String toString() {
            return this.getPositionKey() + ",subAccount=" + this.subaccount
                    + ",CurrencyFree=" + currencyFree.setScale(5, RoundingMode.FLOOR).toString()
                    + ",CurrencyLocked=" + currencyLocked.setScale(5, RoundingMode.FLOOR).toString();
            //+ ",quoteCurrencyFree=" + quoteCurrencyFree.setScale(5, RoundingMode.HALF_EVEN).toString()
            //+ ",quoteCurrencyLocked=" + quoteCurrencyLocked.setScale(5, RoundingMode.HALF_EVEN).toString() ;
        }
    }

    @Data
    private class Order {

        private String orderId;
        private String exchangeOrderId;

        private BigDecimal price;
        private BigDecimal origQty;
        private BigDecimal executedQty;

        private String side;
        private OrderStatus status;
        private long exchangeInsertTime;
        private long exchangeUpdateTime;

        private String subaccountCode;
        private String symbol;
        private String type; //limit or market


        @Override
        public String toString() {
            return orderId + ",subaccountCode=" + subaccountCode + ",symbol=" + symbol + ",side=" + side
                    + ",price=" + price.setScale(5, RoundingMode.FLOOR).toString() + ",origQty=" + origQty.setScale(5, RoundingMode.FLOOR).toString()
                    + ",executedQty=" + executedQty.setScale(5, RoundingMode.FLOOR).toString()
                    + ",status=" + getOrderStatusString(status) + ",IN UPtime=" + exchangeUpdateTime + "," + exchangeInsertTime;
        }
    }

    public enum BuySell {
        Buy, Sell
    }

    public enum OrderStatus {
        SendOut, Submitted, AllTraded, PartlyTraded, Canceled,
        PartlyCanceled, TgConnectionError, ExchRejected, Error
    }

    public enum PositionStatus {
        READY, NOPOSITION, LONGPOSITION, LONGFULLPOSITION, BUYOPEN, SELLCLOSE
    }

    public enum StrategyStatus {
        NoN, Buy, StopBuy, Sell, StopSell
    }


    private String mgHost = "47.91.18.148"; //行情服务器地址
    private String tgHost = "47.74.46.27"; //交易服务器地址

    private int mgport = 8991;
    private int tgport = 8993;
    private String username = "15618929530"; //交易员用户名
    private String password = "147258"; //交易员密码
    private String baseCurrency = "eth";
    private String quoteCurrency = "usdt";

    private long updateTime = 0;

    //Logger
    private ConsoleLogger logger = new ConsoleLogger();
    private BigDecimal maxTradeQuantity = new BigDecimal("0.3"); //下单量
    private BigDecimal rangeDelta = new BigDecimal("2"); // 套利策略运行的空间
    private BigDecimal openRange = new BigDecimal("0.2"); // ask1 与 bid1的差距达到一个gap后才触发交易
    private BigDecimal basePrice = new BigDecimal("-0.1"); // 开仓基准价
    private BigDecimal gDelta = new BigDecimal("0.0");


    //private BigDecimal buyFrozenQty = new BigDecimal("0.2");
    private Map<String, Boolean> frozenOrderActionMap = new HashMap<>(); ////下单前临时冻结量

//  private List<String> exchanges = Arrays.asList("huobi", "binance", "okex"); // 交易所

    //  private List<String> exchanges = Arrays.asList("huobi", "okex"); // 交易所
    //   private List<String> subAccounts = Arrays.asList("Huobi03", "okex03"); // 子账户

    private List<String> exchanges = Arrays.asList("binance", "huobi"); // 交易所
    private List<String> subAccounts = Arrays.asList("Binance-a1_15618929530", "HuoBi-c1_15618929530"); // 子账户

    private Map<String, Common.Symbol> symbols = new HashMap<>(); //交易币对
    private Map<String, Map<String, BigDecimal>> lastCurrencyMap = new HashMap<>(); //最新仓位
    private Map<String, BidAsk1> bidAsk1Map = new HashMap<>(); //最新bid1ask1
    private Map<String, ArbitrageSymbol> arbitrageSymbolMap = new HashMap<>(); //最新arbitrage symbol

    public List<List<String>> records = new ArrayList<>();

    private HashMap<String, Orderreporting.Order> hashOrderMap = new HashMap<>(); //全部的Order,key为orderId，value为Orderreporting.Order
    private OrderMap<String, Orderreporting.Order> orderMap = new OrderMap<>(hashOrderMap); //全部的Order,key为orderId，value为Orderreporting.Order

    private Map<String, Position> positionMap = new HashMap<>(); //全部的Position,key为exchange，value为postion

    public ExpectedOpenClosePrice expectedPrice = new ExpectedOpenClosePrice();
    public String arbitrageName = exchanges.get(0) + "-" + exchanges.get(1) + ":" + baseCurrency + "/" + quoteCurrency;

    private OrderReportingGrpc.OrderReportingStub tgStub; //交易网关
    private MarketDataManageGrpc.MarketDataManageStub mgStub; //行情网关
    private QueryServiceGrpc.QueryServiceStub queryStub; //查询网关
    private QueryServiceGrpc.QueryServiceBlockingStub queryBlockingStub; //查询网关（阻塞）

    //private Map<String, String> orderMap = new HashMap<>(); //等待成交的报单，key为方向(buy/sell)，value为orderId

    public static void main(String[] args) throws InterruptedException {
        SpreadStrategy ta = new SpreadStrategy();
        while (true) {
            Thread.sleep(3000);
        }
    }

    /**
     * 初始化均值计算
     * 初始化三个grpc服务
     * 调用登录接口，获取token
     * 初始化交易标的
     */
    private SpreadStrategy() {

        //初始化交易网关

        ManagedChannel tgChannel = ManagedChannelBuilder.forAddress(tgHost, tgport)
                .keepAliveTime(10, TimeUnit.MINUTES).usePlaintext().build();

        //初始化行情网关
        ManagedChannel mgChannel = ManagedChannelBuilder.forAddress(mgHost, mgport)
                .keepAliveTime(10, TimeUnit.MINUTES).usePlaintext().build();
        mgStub = MarketDataManageGrpc.newStub(mgChannel);


        Runnable tgStateListener = new Runnable() {
            @Override
            public void run() {
                ConnectivityState currentState = tgChannel.getState(true);
                logger.Debug("TG State Changed: " + currentState);
                // 根据最新状态进行处理
                switch (currentState) {
                    case IDLE: //服务器断开了
                        //TODO 停止策略，或者暂停策略，等待连接成功后再恢复
                        logger.Debug("TG Server disconnected");
                        break;
                    case READY: //连接建立成功
                        //TODO 进行重新初始化
                        initTG(tgChannel);
                        initMG(mgChannel);

                        break;
                    case SHUTDOWN: //客户端主动断开
                        //可以忽略
                        break;
                    case CONNECTING: // 正在尝试重连
                        //可以忽略
                        break;
                    case TRANSIENT_FAILURE: //重连失败
                        //可以忽略，还会继续尝试重连的
                        break;
                    default:
                        break;
                }
                tgChannel.notifyWhenStateChanged(currentState, this);
            }
        };
        tgStateListener.run();


        Runnable mgStateListener = new Runnable() {
            @Override
            public void run() {
                ConnectivityState currentState = mgChannel.getState(true);
                logger.Debug("MG State Changed: " + currentState);
                // 根据最新状态进行处理
                switch (currentState) {
                    case IDLE: //服务器断开了
                        //TODO 停止策略，或者暂停策略，等待连接成功后再恢复
                        logger.Debug("MG Server disconnected");
                        break;
                    case READY: //连接建立成功
                        //TODO 进行重新初始化
                        //initMG(mgChannel);
                        break;
                    case SHUTDOWN: //客户端主动断开
                        //可以忽略
                        break;
                    case CONNECTING: // 正在尝试重连
                        //可以忽略
                        break;
                    case TRANSIENT_FAILURE: //重连失败
                        //可以忽略，还会继续尝试重连的
                        break;
                    default:
                        break;
                }
                mgChannel.notifyWhenStateChanged(currentState, this);
            }
        };
        mgStateListener.run();


    }

    /**
     * 订阅成交回报，在回报时维护orderMap,将已成交订单移除
     */

    private void initTG(ManagedChannel tgChannel) {
        //登录获取token
        LoginGrpc.LoginBlockingStub loginBlockingStub = LoginGrpc.newBlockingStub(tgChannel);
        LoginOuterClass.RspLogin rspLogin = loginBlockingStub.login(LoginOuterClass.ReqLogin.newBuilder().setUsername(username).setPassword(password).build());
        String token = rspLogin.getToken();
        //初始化交易网关和查询网关
        tgStub = OrderReportingGrpc.newStub(tgChannel).withCallCredentials(new JwtCallCredential(token));
        queryStub = QueryServiceGrpc.newStub(tgChannel).withCallCredentials(new JwtCallCredential(token));
        queryBlockingStub = QueryServiceGrpc.newBlockingStub(tgChannel).withCallCredentials(new JwtCallCredential(token));


        for (String exchange : exchanges) {
            Common.Symbol symbol = Common.Symbol.newBuilder().setBaseCurrency(baseCurrency).setQuoteCurrency(quoteCurrency).setExchangeType(exchange).build();
            symbols.putIfAbsent(exchange, symbol);
        }

        for (String exchange : exchanges) {
            bidAsk1Map.put(exchange, null);
        }

        for (int i = 0; i < exchanges.size(); i++) {
            for (int j = i + 1; j < exchanges.size(); j++) {
                ArbitrageSymbol symbol = new ArbitrageSymbol(exchanges.get(i), exchanges.get(j), baseCurrency, quoteCurrency);
                String exchangePair = symbol.getArbitrageName();
                arbitrageSymbolMap.put(exchangePair, symbol);
            }
        }

        //查询初始仓位
        Orderreporting.SubaccountResponse subaccountResponse = queryBlockingStub.getSubaccounts(Orderreporting.SubaccountRequest.newBuilder().build());

        for (Orderreporting.Subaccount subaccount : subaccountResponse.getDataList()) {
            String exchange = subaccount.getExchangeType();
            if (exchanges.contains(exchange)) {
                Map<String, BigDecimal> exchangeBalanceMap = new HashMap<>();
                for (Orderreporting.AssetBalance assetBalance : subaccount.getAssetBalanceList()) {
                    if (assetBalance.getAsset().equals(baseCurrency)
                            && (subaccount.getSubaccountCode().equals(subAccounts.get(0)) || subaccount.getSubaccountCode().equals(subAccounts.get(1)))) {
                        exchangeBalanceMap.putIfAbsent(assetBalance.getAsset(), new BigDecimal(assetBalance.getFree()));
                        exchangeBalanceMap.putIfAbsent(assetBalance.getAsset(), new BigDecimal(assetBalance.getLocked()));

                        Position pos = new Position();
                        pos.exchange = exchange;
                        pos.subaccount = subaccount.getSubaccountCode();
                        pos.type = "base";
                        pos.currency = baseCurrency;
                        pos.currencyFree = new BigDecimal(assetBalance.getFree());
                        pos.currencyLocked = new BigDecimal(assetBalance.getLocked());

                        String positionKey = exchange + baseCurrency;
                        pos.positionKey = positionKey;

                        positionMap.putIfAbsent(positionKey, pos);

                        frozenOrderActionMap.putIfAbsent(exchange, false);

                    }
                    if (assetBalance.getAsset().equals(quoteCurrency)
                            && (subaccount.getSubaccountCode().equals(subAccounts.get(0)) || subaccount.getSubaccountCode().equals(subAccounts.get(1)))) {
                        exchangeBalanceMap.putIfAbsent(assetBalance.getAsset(), new BigDecimal(assetBalance.getFree()));
                        exchangeBalanceMap.putIfAbsent(assetBalance.getAsset(), new BigDecimal(assetBalance.getLocked()));

                        Position pos = new Position();
                        pos.exchange = exchange;
                        pos.subaccount = subaccount.getSubaccountCode();
                        pos.type = "quote";
                        pos.currency = quoteCurrency;
                        pos.currencyFree = new BigDecimal(assetBalance.getFree());
                        pos.currencyLocked = new BigDecimal(assetBalance.getLocked());

                        String positionKey = exchange + quoteCurrency;
                        pos.positionKey = positionKey;

                        positionMap.putIfAbsent(positionKey, pos);

                        frozenOrderActionMap.putIfAbsent(exchange, false);

                    }
                }
                if (!exchangeBalanceMap.containsKey(baseCurrency)
                        && (subaccount.getSubaccountCode().equals(subAccounts.get(0)) || subaccount.getSubaccountCode().equals(subAccounts.get(1)))) {
                    exchangeBalanceMap.put(baseCurrency, BigDecimal.ZERO);
                    exchangeBalanceMap.put(baseCurrency, BigDecimal.ZERO);

                    Position pos = new Position();
                    pos.exchange = exchange;
                    pos.subaccount = subaccount.getSubaccountCode();
                    pos.type = "base";
                    pos.currency = baseCurrency;
                    pos.currencyFree = BigDecimal.ZERO;
                    pos.currencyLocked = BigDecimal.ZERO;

                    String positionKey = exchange + baseCurrency;
                    pos.positionKey = positionKey;

                    positionMap.put(positionKey, pos);
                }

                if (!exchangeBalanceMap.containsKey(quoteCurrency)
                        && (subaccount.getSubaccountCode().equals(subAccounts.get(0)) || subaccount.getSubaccountCode().equals(subAccounts.get(1)))) {
                    exchangeBalanceMap.put(quoteCurrency, BigDecimal.ZERO);
                    exchangeBalanceMap.put(quoteCurrency, BigDecimal.ZERO);

                    Position pos = new Position();
                    pos.exchange = exchange;
                    pos.subaccount = subaccount.getSubaccountCode();
                    pos.type = "quote";
                    pos.currency = quoteCurrency;
                    pos.currencyFree = BigDecimal.ZERO;
                    pos.currencyLocked = BigDecimal.ZERO;
                    String positionKey = exchange + quoteCurrency;
                    pos.positionKey = positionKey;

                    positionMap.put(positionKey, pos);
                }
                lastCurrencyMap.put(exchange, exchangeBalanceMap);
            }
        }


        //查询未成交订单信息
        Orderreporting.OrderResponse orderResponse = queryBlockingStub.getOrders(Orderreporting.QryOrderRequest.newBuilder().build());
        for (Orderreporting.Order order : orderResponse.getDataList()) {
            if (order.getOrderStatus().equals("submitted") || order.getOrderStatus().equals("partially_filled")) {
                orderMap.put(order.getOrderId(), order);
            }
        }

        //查询币对信息
        Orderreporting.SymbolResponse symbolResponse = queryBlockingStub.getSymbols(Orderreporting.SymbolRequest.newBuilder().build());

        for (Orderreporting.QrySymbol symbol : symbolResponse.getDataList()) {
            for (Map.Entry<String, ArbitrageSymbol> entry : arbitrageSymbolMap.entrySet()) {
                if (symbol.getSymbol().getBaseCurrency().equals(baseCurrency) && symbol.getSymbol().getQuoteCurrency().equals(quoteCurrency)) {
                    if (symbol.getSymbol().getExchangeType().equals(entry.getValue().ExchangeA)) {
                        Precision precision = new Precision();
                        precision.exchange = entry.getValue().ExchangeA;
                        precision.symbol = symbol.getSymbol();
                        precision.baseCurrency = baseCurrency;
                        precision.quoteCurrency = quoteCurrency;
                        precision.pricePrecision = new BigDecimal(symbol.getPricePrecision());
                        //ETH
                        if(symbol.getSymbol().getExchangeType().equals("binance"))
                            precision.pricePrecision = new  BigDecimal("2");

                        if(precision.pricePrecision.compareTo(new BigDecimal("4")) == 1)
                        {
                            precision.pricePrecision = new BigDecimal("4");
                        }


                        precision.amountPrecision = new BigDecimal(symbol.getAmountPrecision());
                        precision.minAmount = new BigDecimal(symbol.getMinAmount());
                        entry.getValue().aSymbolPrecision = precision;
                    }
                    if (symbol.getSymbol().getExchangeType().equals(entry.getValue().ExchangeB)) {
                        Precision precision = new Precision();
                        precision.exchange = entry.getValue().ExchangeB;
                        precision.symbol = symbol.getSymbol();
                        precision.baseCurrency = baseCurrency;
                        precision.quoteCurrency = quoteCurrency;
                        precision.pricePrecision = new BigDecimal(symbol.getPricePrecision());

                        //ETH
                        if(symbol.getSymbol().getExchangeType().equals("binance"))
                            precision.pricePrecision = new  BigDecimal("2");

                        if(precision.pricePrecision.compareTo(new BigDecimal("4")) == 1)
                        {
                            precision.pricePrecision = new BigDecimal("4");
                        }

                        //precision.amountPrecision = new BigDecimal("8");
                        precision.amountPrecision = new BigDecimal(symbol.getAmountPrecision());
                        precision.minAmount = new BigDecimal(symbol.getMinAmount());

                        entry.getValue().bSymbolPrecision = precision;

                    }
                    logger.Debug("symbol:" + symbol.toString());
                }
            }
        }

        expectedPrice.arbitrageName = arbitrageName;
        expectedPrice.expectedBuyPrice = basePrice;
        expectedPrice.expectedSellPrice = basePrice.add(openRange);
        expectedPrice.expectedStopBuyPrice = expectedPrice.expectedBuyPrice.subtract(rangeDelta);
        expectedPrice.expectedStopSellPrice = expectedPrice.expectedSellPrice.add(rangeDelta);


        subOrder();
        subTrade();
        printAllOrder();
        printAllPosition();

    }

    private void initMG(ManagedChannel mgChannel) {
        subMarketData();
    }


    private void printAllOrder() {
        Orderreporting.Order order = null;
        for (Map.Entry<String, Orderreporting.Order> entry : orderMap.entrySet()) {
            String id = entry.getKey();
            order = entry.getValue();
            logger.Debug("OrderID:" + id + ":" + order.toString());
        }
    }


    private void printAllPosition() {
        for (Map.Entry<String, Position> entry : positionMap.entrySet()) {
            String id = entry.getKey();
            logger.Info("exchange+currency:" + id + ",type:" + entry.getValue().getType() + ",value:" + entry.getValue().toString());
        }
    }

    private void printOrder(String title, Orderreporting.Order order) {
        logger.Info(String.format("[%s] SubAccount:%s Symbol:%s OrderID:%s OrderStatus:%s Qty:%s Price:%s ExcutedQty:%s", title, order.getSubaccountCode(), getSymbolString(order.getSymbol()), order.getOrderId(), order.getOrderStatus(), order.getOrigQty(), order.getPrice(), order.getExecutedQty()));
    }

    private void printTrade(String title, Orderreporting.Trade trade) {
        logger.Info(String.format("[%s] SubAccount:%s Symbol:%s OrderID:%s TradeID:%s Qty:%s Price:%s Commission:%s", title, trade.getSubaccountCode(), getSymbolString(trade.getSymbol()), trade.getOrderId(), trade.getTradeId(), trade.getQuantity(), trade.getPrice(), trade.getCommission()));
    }

    private void printPosition(String title, Position position) {
        logger.Info("[" + title + "] " + position.positionKey + ",type:" + position.getType() + ",value:" + position.toString());
    }

    private String getSymbolString(Common.Symbol symbol) {
        return String.format("%s-%s/%s", symbol.getExchangeType(), symbol.getBaseCurrency(), symbol.getQuoteCurrency());
    }

    private boolean isFullPosition(String exchangeCompare, BigDecimal quantity) {


        boolean isFull = false;
        BigDecimal range90 = new BigDecimal("0.90");

        BigDecimal baseFree = BigDecimal.ZERO;
        BigDecimal baseLocked = BigDecimal.ZERO;
        BigDecimal quoteFree = BigDecimal.ZERO;
        BigDecimal quoteLocked = BigDecimal.ZERO;

        for (Map.Entry<String, Position> entry : positionMap.entrySet()) {
            String id = entry.getKey();

            String exchange = entry.getValue().getExchange();
            String currency = entry.getValue().getCurrency();
            String type = entry.getValue().getType();
            if (exchange.equals(exchangeCompare)) {
                if (currency.equals(baseCurrency)) {
                    baseFree = entry.getValue().getCurrencyFree();
                    baseLocked = entry.getValue().getCurrencyLocked();
                } else if (currency.equals(quoteCurrency)) {
                    quoteFree = entry.getValue().getCurrencyFree();
                    quoteLocked = entry.getValue().getCurrencyLocked();
                }
            }

            logger.Debug("id:" + id + ",type:" + entry.getValue().getType() + ",value:" + entry.getValue().toString());
        }

        if (baseFree.compareTo(quantity.multiply(range90)) == 1)    //baseFree在固定Qty表示交易所持多仓满仓
        {
            isFull = true;
        }

        logger.Debug("isFullPosition():isFull:" + isFull + ",exchange=" + exchangeCompare + ",quantity:" + quantity.toString());

        return isFull;

    }

    private PositionStatus getStatusByPosition(String exchangeCompare, BigDecimal maxTradeQuantity, BigDecimal lastPrice) {

        logger.Debug("getStatusByPosition():exchangeCompare:" + exchangeCompare + ",maxTradeQuantity:" + maxTradeQuantity.toString() + ",lastPrice:" + lastPrice.toString());


        BigDecimal range01 = new BigDecimal("0.01");
        BigDecimal range10 = new BigDecimal("0.10");
        BigDecimal range90 = new BigDecimal("0.90");
        BigDecimal range110 = new BigDecimal("1.10");

        BigDecimal baseFree = BigDecimal.ZERO;
        BigDecimal baseLocked = BigDecimal.ZERO;
        BigDecimal quoteFree = BigDecimal.ZERO;
        BigDecimal quoteLocked = BigDecimal.ZERO;

        for (Map.Entry<String, Position> entry : positionMap.entrySet()) {
            String id = entry.getKey();

            String exchange = entry.getValue().getExchange();
            String currency = entry.getValue().getCurrency();
            String type = entry.getValue().getType();
            if (exchange.equals(exchangeCompare)) {
                if (currency.equals(baseCurrency)) {
                    baseFree = entry.getValue().getCurrencyFree();
                    baseLocked = entry.getValue().getCurrencyLocked();
                } else if (currency.equals(quoteCurrency)) {
                    quoteFree = entry.getValue().getCurrencyFree();
                    quoteLocked = entry.getValue().getCurrencyLocked();
                }
            }
        	/*else if(exchange.equals(exchangeB))
        	{
	        	if(currency.equals(baseCurrency))
	        	{
	        		BbaseFree = entry.getValue().getCurrencyFree();
	        		BbaseLocked = entry.getValue().getCurrencyLocked();
	        	}
	        	else if(currency.equals(quoteCurrency))
	        	{
	        		BquoteFree = entry.getValue().getCurrencyFree();
	        		BquoteLocked = entry.getValue().getCurrencyLocked();
	        	}
        	}*/

            logger.Debug("id:" + id + ",type:" + entry.getValue().getType() + ",value:" + entry.getValue().toString());
        }

        logger.Debug("exchangeCompare:" + exchangeCompare + ",maxTradeQuantity:" + maxTradeQuantity.toString());

        PositionStatus status = PositionStatus.READY;
        //根据资金信息判断当前属于哪种状态

        if (quoteLocked.compareTo(maxTradeQuantity.multiply(range10).multiply(lastPrice)) == 1)          //quoteLocked>0表示有买开单，残余仓位即为0
        {
            boolean hasOrder = false;
            for (Map.Entry<String, Orderreporting.Order> entry : orderMap.entrySet()) {
                Orderreporting.Order order = entry.getValue();
                if (order.getSymbol().getExchangeType().equals(exchangeCompare) && order.getSide().equals("buy")
                        && (order.getOrderStatus().equals("submitted") || order.getOrderStatus().equals("partially_filled"))) {
                    hasOrder = true;
                    break;
                }
            }
            if (hasOrder)
                status = PositionStatus.BUYOPEN;
            else
                status = PositionStatus.NOPOSITION;

        } else if (quoteLocked.compareTo(maxTradeQuantity.multiply(range10.multiply(lastPrice))) == -1) {
            if (baseLocked.compareTo(maxTradeQuantity.multiply(range01)) == 1)   // baseLocked>0表示有卖平单,由于精度问题，残余仓位即为0
            {
                boolean hasOrder = false;
                for (Map.Entry<String, Orderreporting.Order> entry : orderMap.entrySet()) {
                    Orderreporting.Order order = entry.getValue();
                    if (order.getSymbol().getExchangeType().equals(exchangeCompare) && order.getSide().equals("sell")
                            && (order.getOrderStatus().equals("submitted") || order.getOrderStatus().equals("partially_filled"))) {
                        hasOrder = true;
                        break;
                    }
                }
                if (hasOrder)
                    status = PositionStatus.SELLCLOSE;
                else
                    status = PositionStatus.LONGPOSITION;
            } else if (baseLocked.compareTo(maxTradeQuantity.multiply(range01)) == -1) {
                if (baseFree.compareTo(maxTradeQuantity.multiply(range10)) == 1)   //baseFree>0&&baseLocked=0,表示多仓
                {
                    status = PositionStatus.LONGPOSITION;
                }
	           /* else if(baseFree.compareTo(quantity.multiply(range90)) == 1
	            		)    //baseFree在固定Qty表示交易所持多仓满仓
	            {
	            	status = PositionStatus.LONGFULLPOSITION;
	            }*/
                else if ((baseFree.compareTo(maxTradeQuantity.multiply(range01)) == -1
                        || (baseFree.compareTo(maxTradeQuantity.multiply(range01)) == 1 && baseFree.compareTo(maxTradeQuantity.multiply(range10)) == -1)))  //baseFree==0&&baseLocked==0,表示无仓
                {
                    status = PositionStatus.NOPOSITION;
                }
            }
        }

        return status;

        //READY,NOPOSITION,LONGPOSITION,LONGFULLPOSITION,BUYOPEN,SELLCLOSE
    }


    private Orderreporting.Order findOrderByOrderId(String orderId) {
        Orderreporting.Order order = null;
        for (Map.Entry<String, Orderreporting.Order> entry : orderMap.entrySet()) {
            String id = entry.getKey();
            if (id.equals(orderId)) {
                order = entry.getValue();
                break;
            }
        }

        return order;
    }

    private boolean IsFinalState(String status) {
        boolean flag = false;
        OrderStatus orderStatus = getOrderStatusEnum(status);
        if (orderStatus.equals(OrderStatus.Error) || orderStatus.equals(OrderStatus.ExchRejected)
                || orderStatus.equals(OrderStatus.TgConnectionError) || orderStatus.equals(OrderStatus.PartlyCanceled)
                || orderStatus.equals(OrderStatus.Canceled) || orderStatus.equals(OrderStatus.PartlyCanceled)) {
            flag = true;
        }

        logger.Debug("IsFinalState():orderStatus:" + status + ",Enum:" + orderStatus);

        return flag;
    }

    private void subOrder() {
        //订阅委托回报
        StreamObserver<Orderreporting.ReqSubOrder> reqSubOrderStreamObserver = tgStub.subOrder(new StreamObserver<Orderreporting.RtnSubOrder>() {
            @Override
            public void onNext(Orderreporting.RtnSubOrder rtnSubOrder) {
                Orderreporting.Order order = rtnSubOrder.getOrder();
                printOrder("RtnOrder", order);
                Orderreporting.Order oldOrder = findOrderByOrderId(order.getOrderId());

                if (oldOrder == null) {
                    orderMap.put(order.getOrderId(), order);

                    //如果报单不是最终状态
                    if (!IsFinalState(order.getOrderStatus())) {
                        String exchange = order.getSymbol().getExchangeType();
                        Position posBase = positionMap.get(exchange + baseCurrency);
                        Position posQuote = positionMap.get(exchange + quoteCurrency);

                        //计算冻结
                        if (posBase != null && posQuote != null) {
                            //计算冻结
                            if (order.getSide().equals("buy")) {
                                posQuote.setCurrencyLocked(posQuote.getCurrencyLocked()
                                        .add((new BigDecimal(order.getOrigQty())).multiply(new BigDecimal(order.getPrice()))));  //冻结quote

                                posQuote.setCurrencyFree(posQuote.getCurrencyFree()
                                        .subtract((new BigDecimal(order.getOrigQty()).multiply(new BigDecimal(order.getPrice())))));  //减少quote可用

                            } else if (order.getSide().equals("sell")) {
                                posBase.setCurrencyLocked(posBase.getCurrencyLocked()
                                        .add(new BigDecimal(order.getOrigQty())));  //冻结base

                                posBase.setCurrencyFree(posBase.getCurrencyFree()
                                        .subtract(new BigDecimal(order.getOrigQty())));  //减少base可用

                            } else {
                                logger.Warn(String.format("Error Side in trade,orderID:%s, side:%s", order.getOrderId(), order.getSide()));
                            }
                            printPosition("RtnOrder-PositionBase", posBase);
                            printPosition("RtnOrder-PositionQuote", posQuote);
                        } else {
                            logger.Warn("[RtnOrder] base/quote is NULL");
                        }
                    }
                } else {
                    orderMap.put(order.getOrderId(), order);
                    //新报单状态在旧报单之后 且 新报单不是最终状态
                    if (!IsFinalState(oldOrder.getOrderStatus()) && IsFinalState(order.getOrderStatus())) {
                        BigDecimal execcutedQty = new BigDecimal(order.getExecutedQty() == null ? "0" : order.getExecutedQty());
                        BigDecimal remainQty = (new BigDecimal(order.getOrigQty())).subtract(execcutedQty);

                        String exchange = order.getSymbol().getExchangeType();
                        Position posBase = positionMap.get(exchange + baseCurrency);
                        Position posQuote = positionMap.get(exchange + quoteCurrency);

                        if (posBase != null && posQuote != null) {
                            if (order.getSide().equals("buy")) {
                                posQuote.setCurrencyLocked(posQuote.getCurrencyLocked()
                                        .subtract(remainQty.multiply(new BigDecimal(order.getPrice()))));  //解除quote冻结

                                posQuote.setCurrencyFree(posQuote.getCurrencyFree()
                                        .add(remainQty.multiply(new BigDecimal(order.getPrice()))));  //增加quote可用
                            } else if (order.getSide().equals("sell")) {
                                posBase.setCurrencyLocked(posBase.getCurrencyLocked()
                                        .subtract(remainQty));  //解除base冻结

                                posBase.setCurrencyFree(posBase.getCurrencyFree()
                                        .add(remainQty));  //增加base可用
                            } else {
                                logger.Warn(String.format("Error Side in trade,orderID:%s, side:%s", order.getOrderId(), order.getSide()));
                            }
                            printPosition("RtnOrder-PositionBase", posBase);
                            printPosition("RtnOrder-PositionQuote", posQuote);
                        } else {
                            logger.Warn("[RtnOrder] base/quote is NULL");
                        }
            	       /* //解除冻结
            	        if (data.Side == SideType.Buy)
            	        {
            	            quoteAsset.Locked -= (order.OrigQty - (order.ExecuteQty ?? 0)) * order.Price;       //解除quote冻结
            	            quoteAsset.Free += (order.OrigQty - (order.ExecuteQty ?? 0)) * order.Price;         //增加quote可用
            	        }
            	        else
            	        {
            	            baseAsset.Locked -= order.OrigQty.Value - (order.ExecuteQty ?? 0);      //解除base冻结
            	            baseAsset.Free += order.OrigQty.Value - (order.ExecuteQty ?? 0);        //增加base可用
            	        }*/
                    }
                }

                //printAllOrder();
                //printAllPosition();
                //runStrategy();
            }

            @Override
            public void onError(Throwable t) {
                logger.Error("sumOrder,onError");

                t.printStackTrace();
            }

            @Override
            public void onCompleted() {
                logger.Debug("sumOrder,onCompleted");

            }

        });
        //不要漏掉下面这句调用，向服务请求订阅委托回报
        reqSubOrderStreamObserver.onNext(Orderreporting.ReqSubOrder.newBuilder().build());
        logger.Debug("订阅了所有交易所的委托回报");

    }

    private void subTrade() {
        //订阅成交回报
        StreamObserver<Orderreporting.ReqSubTrade> reqSubTradeStreamObserver = tgStub.subTrade(new StreamObserver<Orderreporting.RtnSubTrade>() {
            @Override
            public void onNext(Orderreporting.RtnSubTrade rtnSubTrade) {
                printTrade("RtnTrade", rtnSubTrade.getTrade());
                String symbol = rtnSubTrade.getTrade().getSymbol().toString();
                BigDecimal commission = BigDecimal.ZERO;
                if (rtnSubTrade.getTrade().getCommission() != null) {
                    commission = new BigDecimal(rtnSubTrade.getTrade().getCommission());
                }
                BigDecimal tradePrice = new BigDecimal(rtnSubTrade.getTrade().getPrice());
                BigDecimal quantity = new BigDecimal(rtnSubTrade.getTrade().getQuantity());
                BigDecimal orderPrice = new BigDecimal(rtnSubTrade.getTrade().getOrderPrice());

                String exchange = rtnSubTrade.getTrade().getSymbol().getExchangeType();
                Position posBase = positionMap.get(exchange + baseCurrency);
                Position posQuote = positionMap.get(exchange + quoteCurrency);

                //计算冻结
                if (posBase != null && posQuote != null) {
                    if (rtnSubTrade.getTrade().getSide().equals("buy")) {
                        logger.Debug(exchange + ":" + symbol + "," + quantity + "," + tradePrice + "," + commission + " 买单已成交");
                        posBase.setCurrencyFree(posBase.getCurrencyFree()
                                .add(quantity.subtract(commission)));  //增加base扣除手续费后的可用

                        posQuote.setCurrencyLocked(posQuote.getCurrencyLocked()
                                .subtract(quantity.multiply(orderPrice)));  //使用OrderPrice解除quote冻结

                        posQuote.setCurrencyFree(posQuote.getCurrencyFree()
                                .add(quantity.multiply(tradePrice.subtract(orderPrice))));  //通过计算OrderPrice和TradePrice的差额返回quote可用


                    } else if (rtnSubTrade.getTrade().getSide().equals("sell")) {
                        logger.Debug(exchange + ":" + symbol + "," + quantity + "," + tradePrice + "," + commission + " 卖单已成交");

                        posQuote.setCurrencyFree(posQuote.getCurrencyFree()
                                .add(quantity.multiply(tradePrice).subtract(commission)));  //加quote扣除手续费后的可用

                        posBase.setCurrencyLocked(posBase.getCurrencyLocked()
                                .subtract(quantity));  //解除base冻结
                    } else {
                        logger.Warn(String.format("Error Side in trade,orderID:%s, tradeID:%s  side:%s", rtnSubTrade.getTrade().getOrderId(), rtnSubTrade.getTrade().getTradeId(), rtnSubTrade.getTrade().getSide()));
                    }
                    printPosition("RtnTrade-PositionBase", posBase);
                    printPosition("RtnTrade-PositionQuote", posQuote);
                }else{
                    logger.Warn("[RtnTrade] base/quote is NULL");
                }
                //printAllPosition();
                //runStrategy();
            }

            @Override
            public void onError(Throwable t) {

                logger.Error("sumTrade,onError");

                t.printStackTrace();
            }

            @Override
            public void onCompleted() {
                logger.Debug("subTrade,onCompleted");

            }
        });
        //不要漏掉下面这句调用，向服务请求订阅成交回报
        reqSubTradeStreamObserver.onNext(Orderreporting.ReqSubTrade.newBuilder().build());
        logger.Debug("订阅了所有交易所的成交回报");
    }


    /**
     * 订阅行情，比较不同交易所的ask1与bid1，
     */
    private void subMarketData() {
        StreamObserver<MarketGateway.ReqSubDepth> requestObserver = mgStub.subDepth(new StreamObserver<MarketGateway.DepthsDataResponse>() {
            @Override
            public void onNext(MarketGateway.DepthsDataResponse depthsDataResponse) {
                if (!depthsDataResponse.getCRsp().getIsSuccess()) {
                    return;
                }
                MarketGateway.DepthsData depthsData = depthsDataResponse.getData();
                String exchange = depthsData.getSymbol().getExchangeType();
                BidAsk1 bidAsk1 = new BidAsk1(depthsData.getSymbol().getExchangeType(), new BigDecimal(depthsData.getBids(0).getPrice()),
                        new BigDecimal(depthsData.getBids(0).getQuantity()), new BigDecimal(depthsData.getAsks(0).getPrice()),
                        new BigDecimal(depthsData.getAsks(0).getQuantity()), depthsData.getTimestamp());
                // logger.Debug(bidAsk1);
                bidAsk1Map.put(exchange, bidAsk1);
                for (Map.Entry<String, ArbitrageSymbol> entry : arbitrageSymbolMap.entrySet()) {
                    if (!bidAsk1.equals(entry.getValue().exchangABidAsk1) || !bidAsk1.equals(entry.getValue().exchangBBidAsk1)) {
                        entry.getValue().update(exchange, bidAsk1);
                    }

                    if (entry.getKey() != null) {
                        if (arbitrageName.equals(entry.getKey())) {
                            ArbitrageSymbol arbitrageSymbol = entry.getValue();
                            logger.Debug("Before changed:" + arbitrageSymbol.toString() + "  " + expectedPrice.toString());
                            if (arbitrageSymbol.getArbitrageAskPrice() != null && arbitrageSymbol.getArbitrageBidPrice() != null
                                    && arbitrageSymbol.getArbitrageBidQuantity().compareTo(BigDecimal.ZERO) == 1 && arbitrageSymbol.getArbitrageAskQuantity().compareTo(BigDecimal.ZERO) == 1) {
                                runStrategy(arbitrageSymbol);
                            }
                        }
                    }

                }

            }

            @Override
            public void onError(Throwable t) {
                t.printStackTrace();
            }

            @Override
            public void onCompleted() {
                logger.Debug("subData,onCompleted");

            }
        });
        for (Map.Entry<String, Common.Symbol> entry : symbols.entrySet()) {
            logger.Debug("订阅了" + entry.getValue().getExchangeType() + "交易所的行情");
            MarketGateway.ReqSubDepth reqSubDepth = MarketGateway.ReqSubDepth.newBuilder().setSymbol(entry.getValue()).build();
            requestObserver.onNext(reqSubDepth);
        }
    }


    /**
     * 工具函数：查询当前持仓状态
     */

    private BigDecimal getBaseCurrency(String exchange, String baseCurrency) {
        Map<String, BigDecimal> balanceMap = lastCurrencyMap.get(exchange);
        BigDecimal baseBalance = balanceMap.get(baseCurrency);
        return baseBalance;

    }

    private BigDecimal getQuoteCurrency(String exchange, String quoteCurrency) {
        Map<String, BigDecimal> balanceMap = lastCurrencyMap.get(exchange);
        //BigDecimal baseBalance = balanceMap.get(baseCurrency);
        BigDecimal quoteBalance = balanceMap.get(quoteCurrency);
        return quoteBalance;

    }


    /**
     * 工具函数：查询当前持仓状态
     */

    private PositionStatus getStateByPosition(String exchange, String subaccount, String currency, String quantity) {

        PositionStatus status = PositionStatus.READY;
        Map<String, BigDecimal> balanceMap = lastCurrencyMap.get(exchange);
        if (!balanceMap.isEmpty()) {
            BigDecimal balance = balanceMap.get(currency);

            double qty = new BigDecimal(quantity).doubleValue();
            if (balance.doubleValue() <= qty * 0.1) {
                status = PositionStatus.NOPOSITION;
            } else if (qty * 0.1 < balance.doubleValue() && balance.doubleValue() <= qty * 0.9) {
                status = PositionStatus.LONGPOSITION;
            } else if (qty * 0.9 < balance.doubleValue()) {
                status = PositionStatus.LONGFULLPOSITION;
            }

        }

        return status;

    }


    /**
     * 初始化行情记录的数据结构
     */
    private void initRecord() {
        List<String> l = new ArrayList<>();
        for (Map.Entry<String, ArbitrageSymbol> entry : arbitrageSymbolMap.entrySet()) {
            ArbitrageSymbol arbitrageSymbol = entry.getValue();
            l.add(arbitrageSymbol.getArbitrageName() + ":" + "Bid");
            l.add(arbitrageSymbol.getArbitrageName() + ":" + "BidQ");
            l.add(arbitrageSymbol.getArbitrageName() + ":" + "Ask");
            l.add(arbitrageSymbol.getArbitrageName() + ":" + "AskQ");
        }
        records.add(l);
    }

    /**
     * 初始化行情记录的数据结构
     */
    private void record() {
        List<String> l = new ArrayList<>();
        for (Map.Entry<String, ArbitrageSymbol> entry : arbitrageSymbolMap.entrySet()) {
            ArbitrageSymbol arbitrageSymbol = entry.getValue();
            l.add(arbitrageSymbol.getArbitrageBidPrice().setScale(5, RoundingMode.FLOOR).toString());
            l.add(arbitrageSymbol.getArbitrageBidQuantity().setScale(5, RoundingMode.FLOOR).toString());
            l.add(arbitrageSymbol.getArbitrageAskPrice().setScale(5, RoundingMode.FLOOR).toString());
            l.add(arbitrageSymbol.getArbitrageAskQuantity().setScale(5, RoundingMode.FLOOR).toString());
        }
        records.add(l);
    }


    private String getTradeQuantity(ArbitrageSymbol arbitrageSymbol, String exchange, BigDecimal qty) {
        String tradeQ = "0";
        BigDecimal tradeQty = BigDecimal.ZERO;

        BigDecimal range10 = new BigDecimal("0.10");
        BigDecimal range99 = new BigDecimal("0.90");
        BigDecimal range101 = new BigDecimal("1.01");

        for (Map.Entry<String, ArbitrageSymbol> entry : arbitrageSymbolMap.entrySet()) {
            if (entry.getKey() != null) {
                if (arbitrageSymbol.equals(entry.getValue())) {
                    if (exchange.equals(entry.getValue().getExchangeA())) {
                        Precision aPrecision = entry.getValue().getASymbolPrecision();
                        BigDecimal amountPrecision = aPrecision.amountPrecision;
                        BigDecimal minAmount = aPrecision.minAmount;

                        BigDecimal maxTradeQty = minAmount.max(qty);
                        tradeQty = new BigDecimal(String.valueOf(maxTradeQty)).setScale(amountPrecision.intValue(), RoundingMode.FLOOR);

                        logger.Debug("getExchangeA=" + exchange + ",amountPrecision=" + amountPrecision.toString() + ",minAmount=" + minAmount.toString()
                                + ",tradeQty=" + tradeQty);

                    } else if (exchange.equals(entry.getValue().getExchangeB())) {
                        Precision bPrecision = entry.getValue().getBSymbolPrecision();
                        BigDecimal amountPrecision = bPrecision.amountPrecision;
                        BigDecimal minAmount = bPrecision.minAmount;

                        BigDecimal maxTradeQty = minAmount.max(qty);

                        tradeQty = new BigDecimal(String.valueOf(maxTradeQty)).setScale(amountPrecision.intValue(), RoundingMode.FLOOR);

                        logger.Debug("getExchangeB=" + exchange + ",amountPrecision=" + amountPrecision.toString() + ",minAmount=" + minAmount.toString()
                                + ",tradeQty=" + tradeQty);
                    }
                }
            }
        }


        if (tradeQty.compareTo(maxTradeQuantity.multiply(range10)) == -1) {
            tradeQ = "0";
        } else {
            tradeQ = tradeQty.stripTrailingZeros().toPlainString();
        }


        logger.Debug("getTradeQuantity():exchange=" + exchange + ",qty=" + qty.toString() + ",tradeQty=" + tradeQty);

        return tradeQty.stripTrailingZeros().toPlainString();
    }

    private String getTradePrice(ArbitrageSymbol arbitrageSymbol, String exchange, String buySell) {
        BigDecimal tradePrice = BigDecimal.ZERO;
        String tradeP = "0";
        BigDecimal traderDelta = gDelta;
        for (Map.Entry<String, ArbitrageSymbol> entry : arbitrageSymbolMap.entrySet()) {
            if (entry.getKey() != null) {
                if (arbitrageSymbol.equals(entry.getValue())) {
                    if (exchange.equals(entry.getValue().getExchangeA())) {
                        Precision aPrecision = entry.getValue().getASymbolPrecision();
                        BigDecimal pricePrecision = aPrecision.pricePrecision;
                        BigDecimal delta = traderDelta.divide(new BigDecimal(10).pow(pricePrecision.intValue()));

                        if (buySell.equals("buy")) {
                            tradePrice = entry.getValue().getExchangABidAsk1().ask1Price.subtract(delta);

                        } else if (buySell.equals("sell")) {
                            tradePrice = entry.getValue().getExchangABidAsk1().bid1Price.add(delta);
                        }


                        tradePrice.setScale(pricePrecision.intValue(), RoundingMode.FLOOR);

                    } else if (exchange.equals(entry.getValue().getExchangeB())) {

                        Precision bPrecision = entry.getValue().getBSymbolPrecision();

                        BigDecimal pricePrecision = bPrecision.pricePrecision;



                        BigDecimal delta = traderDelta.divide(new BigDecimal(10).pow(pricePrecision.intValue()));

                        if (buySell.equals("buy")) {
                            tradePrice = entry.getValue().getExchangBBidAsk1().ask1Price.subtract(delta);
                            ;
                        } else if (buySell.equals("sell")) {
                            tradePrice = entry.getValue().getExchangBBidAsk1().bid1Price.add(delta);
                        }



                        tradePrice.setScale(pricePrecision.intValue(), RoundingMode.FLOOR);
                    }
                }
            }
        }


        tradeP = tradePrice.stripTrailingZeros().toPlainString();
        logger.Debug("getTradePrice():exchange=" + exchange + ",tradePrice=" + tradePrice.toString() + ",tradeP=" + tradeP);


        return tradeP;
    }

    /**
     * 处理最新的bid1 ask1的数据，决定是否要进行交易
     */
    private void runStrategy(ArbitrageSymbol arbitrageSymbol) {

        StrategyStatus oldStatus = arbitrageSymbol.status;

        if (arbitrageSymbol.getArbitrageAskPrice().compareTo(expectedPrice.expectedBuyPrice) == -1) {

            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                arbitrageSymbol.previousStatus = StrategyStatus.Buy;
            }

            arbitrageSymbol.status = StrategyStatus.Buy;
        } else if (arbitrageSymbol.getArbitrageBidPrice().compareTo(expectedPrice.expectedSellPrice) == 1) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                arbitrageSymbol.previousStatus = StrategyStatus.Sell;
            }

            arbitrageSymbol.status = StrategyStatus.Sell;
        } else if (arbitrageSymbol.getArbitrageAskPrice().compareTo(expectedPrice.expectedStopBuyPrice) == -1) {
            //arbitrageSymbol.previousStatus = arbitrageSymbol.status;
            //arbitrageSymbol.status = StrategyStatus.StopBuy;
        } else if (arbitrageSymbol.getArbitrageBidPrice().compareTo(expectedPrice.expectedSellPrice) == 1) {
            //arbitrageSymbol.previousStatus = arbitrageSymbol.status;
            //arbitrageSymbol.status = StrategyStatus.StopSell;
        } else if ((arbitrageSymbol.getArbitrageAskPrice().compareTo(expectedPrice.expectedBuyPrice) == 1
                && arbitrageSymbol.getArbitrageAskPrice().compareTo(expectedPrice.expectedSellPrice) == -1)
                || (arbitrageSymbol.getArbitrageBidPrice().compareTo(expectedPrice.expectedBuyPrice) == 1
                && arbitrageSymbol.getArbitrageBidPrice().compareTo(expectedPrice.expectedSellPrice) == -1)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                arbitrageSymbol.previousStatus = StrategyStatus.Buy;
            } else if (arbitrageSymbol.status.equals(StrategyStatus.NoN)) {
                //arbitrageSymbol.previousStatus = StrategyStatus.NoN;
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                arbitrageSymbol.previousStatus = StrategyStatus.Sell;
            }

            if (arbitrageSymbol.previousStatus.equals(StrategyStatus.Buy) || arbitrageSymbol.previousStatus.equals(StrategyStatus.Sell)) {
                //不更新previous
            }

            arbitrageSymbol.status = StrategyStatus.NoN;
        }

        PositionStatus statusA = getStatusByPosition(exchanges.get(0), maxTradeQuantity, arbitrageSymbol.exchangABidAsk1.ask1Price);
        PositionStatus statusB = getStatusByPosition(exchanges.get(1), maxTradeQuantity, arbitrageSymbol.exchangBBidAsk1.ask1Price);
        logger.Debug("StrategyStatus():" + arbitrageSymbol.status + ",statusA=" + statusA + ",statusB=" + statusB);
        logger.Debug("Data():B,A=" + arbitrageSymbol.getArbitrageBidPrice() + "," + arbitrageSymbol.getArbitrageAskPrice() + ",Expected,B,A=" + expectedPrice.expectedBuyPrice.toString() + "," + expectedPrice.expectedSellPrice.toString());


        if (!oldStatus.equals(arbitrageSymbol.status))
        {
            logger.Warn("After changed:" + arbitrageSymbol.toString() + "  " + expectedPrice.toString());
            logger.Warn("StrategyStatus():" + arbitrageSymbol.status + ",statusA=" + statusA + ",statusB=" + statusB);
            logger.Warn("Data():B,A=" + arbitrageSymbol.getArbitrageBidPrice() + "," + arbitrageSymbol.getArbitrageAskPrice() + ",Expected,B,A=" + expectedPrice.expectedBuyPrice.toString() + "," + expectedPrice.expectedSellPrice.toString());

        }



        if (statusA.equals(PositionStatus.NOPOSITION) && statusB.equals(PositionStatus.NOPOSITION)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                //Buy A
                String ABuyPrice = getTradePrice(arbitrageSymbol, exchanges.get(0), "buy");
                BigDecimal AFreeQty = positionMap.get(exchanges.get(0) + baseCurrency).getCurrencyFree();
                String expectedBuyQty = getTradeQuantity(arbitrageSymbol, exchanges.get(0), maxTradeQuantity.subtract(AFreeQty));
                if (canSubmitOrder(exchanges.get(0), positionMap.get(exchanges.get(0) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(0)), "buy", arbitrageSymbol.getUpdateTime())) {
                    this.buySell(exchanges.get(0), positionMap.get(exchanges.get(0) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(0)), "buy", ABuyPrice, expectedBuyQty);
                }
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                //Buy B
                String bBuyPrice = getTradePrice(arbitrageSymbol, exchanges.get(1), "buy");
                BigDecimal BFreeQty = positionMap.get(exchanges.get(1) + baseCurrency).getCurrencyFree();
                String expectedBuyQty = getTradeQuantity(arbitrageSymbol, exchanges.get(1), maxTradeQuantity.subtract(BFreeQty));
                if (canSubmitOrder(exchanges.get(1), positionMap.get(exchanges.get(1) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(1)), "buy", arbitrageSymbol.getUpdateTime())) {
                    this.buySell(exchanges.get(1), positionMap.get(exchanges.get(1) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(1)), "buy", bBuyPrice, expectedBuyQty);
                }
            }
        } else if (statusA.equals(PositionStatus.LONGPOSITION) && statusB.equals(PositionStatus.NOPOSITION)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                if (!isFullPosition(exchanges.get(0), maxTradeQuantity)) {
                    //Buy A
                    String ABuyPrice = getTradePrice(arbitrageSymbol, exchanges.get(0), "buy");
                    BigDecimal AFreeQty = positionMap.get(exchanges.get(0) + baseCurrency).getCurrencyFree();
                    String expectedBuyQty = getTradeQuantity(arbitrageSymbol, exchanges.get(0), maxTradeQuantity.subtract(AFreeQty));
                    if (canSubmitOrder(exchanges.get(0), positionMap.get(exchanges.get(0) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(0)), "buy", arbitrageSymbol.getUpdateTime())) {
                        this.buySell(exchanges.get(0), positionMap.get(exchanges.get(0) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(0)), "buy", ABuyPrice, expectedBuyQty);
                    }
                }
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                //Sell A
                String ASellPrice = getTradePrice(arbitrageSymbol, exchanges.get(0), "sell");
                BigDecimal AFreeQty = positionMap.get(exchanges.get(0) + baseCurrency).getCurrencyFree();
                String expectedSellQty = getTradeQuantity(arbitrageSymbol, exchanges.get(0), AFreeQty);
                if (canSubmitOrder(exchanges.get(0), positionMap.get(exchanges.get(0) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(0)), "sell", arbitrageSymbol.getUpdateTime())) {
                    this.buySell(exchanges.get(0), positionMap.get(exchanges.get(0) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(0)), "sell", ASellPrice, expectedSellQty);
                }
            }
        } else if (statusA.equals(PositionStatus.LONGPOSITION) && statusB.equals(PositionStatus.LONGPOSITION)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                //Sell B
                String BSellPrice = getTradePrice(arbitrageSymbol, exchanges.get(1), "sell");
                BigDecimal BFreeQty = positionMap.get(exchanges.get(1) + baseCurrency).getCurrencyFree();

                String expectedSellQty = getTradeQuantity(arbitrageSymbol, exchanges.get(1), BFreeQty);

                if (canSubmitOrder(exchanges.get(1), positionMap.get(exchanges.get(1) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(1)), "sell", arbitrageSymbol.getUpdateTime())) {
                    this.buySell(exchanges.get(1), positionMap.get(exchanges.get(1) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(1)), "sell", BSellPrice, expectedSellQty);
                }
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                //Sell A
                String ASellPrice = getTradePrice(arbitrageSymbol, exchanges.get(0), "sell");
                BigDecimal AFreeQty = positionMap.get(exchanges.get(0) + baseCurrency).getCurrencyFree();
                String expectedSellQty = getTradeQuantity(arbitrageSymbol, exchanges.get(0), AFreeQty);
                if (canSubmitOrder(exchanges.get(0), positionMap.get(exchanges.get(0) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(0)), "sell", arbitrageSymbol.getUpdateTime())) {
                    this.buySell(exchanges.get(0), positionMap.get(exchanges.get(0) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(0)), "sell", ASellPrice, expectedSellQty);
                }
            }
        } else if (statusA.equals(PositionStatus.NOPOSITION) && statusB.equals(PositionStatus.LONGPOSITION)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                //Sell B
                String BSellPrice = getTradePrice(arbitrageSymbol, exchanges.get(1), "sell");
                BigDecimal BFreeQty = positionMap.get(exchanges.get(1) + baseCurrency).getCurrencyFree();

                String expectedSellQty = getTradeQuantity(arbitrageSymbol, exchanges.get(1), BFreeQty);

                if (canSubmitOrder(exchanges.get(1), positionMap.get(exchanges.get(1) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(1)), "sell", arbitrageSymbol.getUpdateTime())) {
                    this.buySell(exchanges.get(1), positionMap.get(exchanges.get(1) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(1)), "sell", BSellPrice, expectedSellQty);
                }
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                if (!isFullPosition(exchanges.get(1), maxTradeQuantity)) {
                    //Buy B
                    String bBuyPrice = getTradePrice(arbitrageSymbol, exchanges.get(1), "buy");
                    BigDecimal BFreeQty = positionMap.get(exchanges.get(1) + baseCurrency).getCurrencyFree();
                    String expectedBuyQty = getTradeQuantity(arbitrageSymbol, exchanges.get(1), maxTradeQuantity.subtract(BFreeQty));
                    if (canSubmitOrder(exchanges.get(1), positionMap.get(exchanges.get(1) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(1)), "buy", arbitrageSymbol.getUpdateTime())) {
                        this.buySell(exchanges.get(1), positionMap.get(exchanges.get(1) + baseCurrency).getSubaccount(), symbols.get(exchanges.get(1)), "buy", bBuyPrice, expectedBuyQty);
                    }
                }
            }
        } else if (statusA.equals(PositionStatus.BUYOPEN) && statusB.equals(PositionStatus.NOPOSITION)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                cancelAllOrderByCondition(arbitrageSymbol, exchanges.get(0), "buy", arbitrageSymbol.getUpdateTime());
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                cancelAllOrder(symbols.get(exchanges.get(0)), "buy");
            } else {
                cancelAllOrder(symbols.get(exchanges.get(0)), "buy");
            }
        } else if (statusA.equals(PositionStatus.NOPOSITION) && statusB.equals(PositionStatus.BUYOPEN)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                cancelAllOrder(symbols.get(exchanges.get(1)), "buy");
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                cancelAllOrderByCondition(arbitrageSymbol, exchanges.get(1), "buy", arbitrageSymbol.getUpdateTime());
            } else {
                cancelAllOrder(symbols.get(exchanges.get(1)), "buy");
            }
        } else if (statusA.equals(PositionStatus.SELLCLOSE) && statusB.equals(PositionStatus.NOPOSITION)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                cancelAllOrder(symbols.get(exchanges.get(0)), "sell");
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                cancelAllOrderByCondition(arbitrageSymbol, exchanges.get(0), "sell", arbitrageSymbol.getUpdateTime());
            } else {
                cancelAllOrder(symbols.get(exchanges.get(0)), "sell");
            }
        } else if (statusA.equals(PositionStatus.NOPOSITION) && statusB.equals(PositionStatus.SELLCLOSE)) {
            if (arbitrageSymbol.status.equals(StrategyStatus.Buy)) {
                cancelAllOrderByCondition(arbitrageSymbol, exchanges.get(1), "sell", arbitrageSymbol.getUpdateTime());
            } else if (arbitrageSymbol.status.equals(StrategyStatus.Sell)) {
                cancelAllOrder(symbols.get(exchanges.get(1)), "sell");
            } else {
                cancelAllOrder(symbols.get(exchanges.get(1)), "sell");
            }
        } else {
            logger.Debug("未知状态:" + arbitrageSymbol.status + ",statusA=" + statusA + ",statusB=" + statusB);

        }

    }


    private void printAllArbitrageHD() {
        for (Map.Entry<String, ArbitrageSymbol> entry : arbitrageSymbolMap.entrySet()) {
            if (entry.getValue() != null) {
                ArbitrageSymbol arbitrageSymbol = entry.getValue();
                logger.Debug(arbitrageSymbol.toString());
                // logger.Debug(entry.getValue());
            }
        }
    }


    private boolean cancelAllOrderByCondition(ArbitrageSymbol arbitrageSymbol, String exchange, String buySell, long updateTime) {
        boolean flag = false;

        for (Map.Entry<String, Orderreporting.Order> entry : orderMap.entrySet()) {

            String orderId = entry.getKey();
            Orderreporting.Order order = entry.getValue();
            long insertTime = order.getExchangeInsertTime();
            String insertPrice = order.getPrice();
            String expectedTradePrice = this.getTradePrice(arbitrageSymbol, exchange, buySell);

            if (order.getSymbol().getExchangeType().equals(exchange) &&  (order.getOrderStatus().equals("submitted") || order.getOrderStatus().equals("partially_filled"))

                    && updateTime > insertTime + 5000) {
                if (order.getSide().equals("buy") && (new BigDecimal(insertPrice).compareTo(new BigDecimal(expectedTradePrice)) == -1)) {
                    flag = true;
                } else if (order.getSide().equals("sell") && (new BigDecimal(insertPrice).compareTo(new BigDecimal(expectedTradePrice)) == 1)) {
                    flag = true;

                }
                if (flag) {
                    this.cancel(orderId);
                    logger.Debug("cancelAllOrderByCondition() orderId=" + orderId + ",exchange=" + exchange + ",buySell=" + buySell + ",updateTime=" + updateTime + ",tradeP=" + expectedTradePrice);
                }
            }
        }

        return flag;
    }

    private boolean canSubmitOrder(String exchange, String subaccountCode, Common.Symbol symbol, String buySell, long updateTime) {
        boolean flag = false, hasSubmittedOrder = false;

        long lastInsertTime = 0;
        for (Map.Entry<String, Orderreporting.Order> entry : orderMap.entrySet()) {
            Orderreporting.Order order = entry.getValue();

            if (order.getSymbol().getExchangeType().equals(exchange) && order.getSide().equals(buySell)) {
                if (order.getOrderStatus().equals("submitted")) {
                    hasSubmittedOrder = true;
                    break;
                }

            }
        }

        if (!hasSubmittedOrder) {
            for (Map.Entry<String, Orderreporting.Order> entry : orderMap.entrySet()) {
                Orderreporting.Order order = entry.getValue();
                if (order.getSide().equals(buySell) && order.getSymbol().getExchangeType().equals(exchange)) {
                    long insertTime = order.getExchangeInsertTime();
                    if (insertTime > lastInsertTime) {
                        lastInsertTime = insertTime;
                    }
                }
            }
            if (updateTime > lastInsertTime + 3000)
                flag = true;
        } else {
            flag = false;
        }

        logger.Debug("canSubmitOrder() flag=" + flag + ",exchange=" + exchange + ",hasOrder=" + hasSubmittedOrder + ",buySell=" + buySell + ",updateTime=" + updateTime + ",lastInsertTime=" + lastInsertTime);

        return flag;

    }

    /**
     * 调用接口进行限价买入卖出，收到响应后将orderId放入orderMap中
     */
    private void buySell(String exchange, String subaccountCode, Common.Symbol symbol, String buySell, String price, String quantity) {
        final Orderreporting.InsertOrderRequest orderTradeRequest = Orderreporting.InsertOrderRequest.newBuilder().
                setSubaccountCode(subaccountCode).setSymbol(symbol).setQuantity(quantity).setPrice(price).setType("limit").setSide(buySell).build();

        Boolean frozen = frozenOrderActionMap.get(exchange);

        if (frozen == false) {
            logger.Info("before insertOrder():exchange=" + symbol.getExchangeType() + ",buysell=" + buySell + ",price=" + price + ",qty=" + quantity + ",frozen=" + frozen);

            frozenOrderActionMap.put(exchange, true);

            Orderreporting.Order newOrder = Orderreporting.Order.newBuilder().setSymbol(symbol).setOrigQty(quantity).setPrice(price).setSide(buySell).
                    setType("limit").setOrderStatus("submitted").build();

            tgStub.insertOrder(orderTradeRequest, new StreamObserver<Orderreporting.InsertOrderResponse>() {
                @Override
                public void onNext(Orderreporting.InsertOrderResponse value) {

                    logger.Debug("after insertOrder():exchange=" + symbol.getExchangeType() + ",buysell=" + buySell + ",price=" + price + ",qty=" + quantity + ",frozen=" + frozen);


                    if (!value.getCRsp().getIsSuccess()) {
                        logger.Info("报单失败: " + value.getCRsp().getErrMsg());
                        frozenOrderActionMap.put(exchange, false);
                    } else {
                        logger.Info("报单成功:" + value.getOrderId());
                        frozenOrderActionMap.put(exchange, false);

                        orderMap.put(value.getOrderId(), newOrder);
                        Position posBase = positionMap.get(exchange + baseCurrency);
                        Position posQuote = positionMap.get(exchange + quoteCurrency);
                        //报单成功后计算冻结
                        if (buySell.equals("buy")) {
                            if(posQuote!=null) {
                                logger.Debug("买单已成交");

                                posQuote.setCurrencyLocked(posQuote.getCurrencyLocked()
                                        .add(new BigDecimal(quantity).multiply(new BigDecimal(price)))); //冻结quote

                                posQuote.setCurrencyFree(posQuote.getCurrencyFree()
                                        .subtract(new BigDecimal(quantity).multiply(new BigDecimal(price))));   //减少quote可用
                                printPosition("InsertOrder-PositionQuote", posQuote);
                            }else{
                                logger.Warn(String.format("InsertOrder %s,%s Position is NULL",exchange,quoteCurrency));
                            }
                        } else if (buySell.equals("sell")) {
                            if(posBase!=null) {
                                logger.Debug("卖单已成交");

                                posBase.setCurrencyLocked(posBase.getCurrencyLocked()
                                        .add(new BigDecimal(quantity)));  //冻结base

                                posBase.setCurrencyFree(posBase.getCurrencyFree()
                                        .subtract(new BigDecimal(quantity))); //减少base可用
                                printPosition("InsertOrder-PositionBase", posBase);
                            }else{
                                logger.Warn(String.format("InsertOrder %s,%s Position is NULL",exchange,quoteCurrency));
                            }
                        }

                        //printAllOrder();
                        //printAllPosition();

                    }
                }

                @Override
                public void onError(Throwable t) {

                    frozenOrderActionMap.put(exchange, false);

                    logger.Error("报单失败: " + t.toString());
                }

                @Override
                public void onCompleted() {

                }
            });

        } else {
            //waiting for
        }

    }

    /*   */

    /**
     * 调用接口进行限价卖出
     *//*
    private void sell(String exchange,String subaccountCode,String symbol,String price,String quantity)
    {
        //String price = lastPrice.setScale(2, RoundingMode.HALF_EVEN).toString();
    	Common.Symbol commonSymbol = symbols.get(exchange);
    	//if(commonSymbol.to)
        final Orderreporting.InsertOrderRequest orderTradeRequest =Orderreporting.InsertOrderRequest.newBuilder().
                setSubaccountCode(subaccountCode).setSymbol(commonSymbol).setQuantity(quantity).setPrice(price).setType("limit").setSide("sell").build();
        logger.Debug("报单卖出： 价格" + price);
        tgStub.insertOrder(orderTradeRequest, new StreamObserver<Orderreporting.InsertOrderResponse>() {
            @Override
            public void onNext(Orderreporting.InsertOrderResponse value)
            {
                if(!value.getCRsp().getIsSuccess()){
                    logger.Debug("报单失败: " + value.getCRsp().getErrMsg());
                }
                else
                {
                    logger.Debug("报单成功：" + value.getOrderId());
                }
            }

            @Override
            public void onError(Throwable t) {
                logger.Debug("报单失败: " + t.getMessage());
            }

            @Override
            public void onCompleted() {

            }
        });
    }*/
    private void cancelAllOrder(Common.Symbol symbol, String buySell) {
        for (Map.Entry<String, Orderreporting.Order> entry : orderMap.entrySet()) {
            String id = entry.getKey();
            Orderreporting.Order order = entry.getValue();
            if (order.getOrderStatus().equals("submitted") || order.getOrderStatus().equals("partially_filled"))
            {
                if (order.getSymbol().equals(symbol) && order.getSide().equals(buySell)) {
                    cancel(order.getOrderId());
                    logger.Debug("canceled: " + id);

                }
            }
        }

    }

    /**
     * 撤单，收到响应后，将订单从orderMap中移除
     *
     * @param orderId 订单ID
     */
    private void cancel(String orderId) {
        //if(orderId.equals("placeHolderOrderId")) //报单还未受理，不需要撤单
        //     return;
        final Orderreporting.CancelOrderRequest cancelOrderRequest = Orderreporting.CancelOrderRequest.newBuilder().setOrderId(orderId).build();
        tgStub.cancelOrder(cancelOrderRequest, new StreamObserver<Orderreporting.CancelOrderResponse>() {
            @Override
            public void onNext(Orderreporting.CancelOrderResponse value) {
                if (value.getCRsp().getIsSuccess()) {
                    logger.Debug("撤单成功:" + value.toString());

                   /* if(value.getData().getOrderId().equals(orderMap.get("buy")))
                    {
                        logger.Debug("取消买单成功");
                        orderMap.remove("buy");
                    }
                    else if(value.getData().getOrderId().equals(orderMap.get("sell")))
                    {
                        logger.Debug("取消卖单成功");
                        orderMap.remove("sell");
                    }*/
                } else {
                    logger.Debug("撤单失败： " + value.getCRsp().getErrMsg());
                }
            }

            @Override
            public void onError(Throwable t) {
                t.printStackTrace();
            }

            @Override
            public void onCompleted() {

            }
        });
    }


//
//    /**
//     * 判断移动平均线是否交叉
//     * 0 未交叉
//     * -1 短周期下穿长周期
//     * 1 短周期上穿长周期
//     * @return 移动平均线是否交叉
//     */
//    private int checkPriceDiff() {
//        int result = 0;
//        BigDecimal longTimeMaValue = longTimeMa.getAvg();
//        BigDecimal shortTimeMaValue = shortTimeMa.getAvg();
//        if(longTimeMaValue.compareTo(BigDecimal.ZERO) > 0 && shortTimeMaValue.compareTo(BigDecimal.ZERO) > 0){
//            BigDecimal newPriceDiff = shortTimeMaValue.subtract(longTimeMaValue);
//            if(priceDiff != null){
//                logger.Debug("previous price diff : " + priceDiff + ", latest price diff: " + newPriceDiff + ", shortPeriod: " + shortTimeMaValue + ", longPeriod" + longTimeMaValue);
//                if(priceDiff.compareTo(BigDecimal.ZERO)<0 && newPriceDiff.compareTo(BigDecimal.ZERO) > 0){
//                    result = 1;
//                }else if (priceDiff.compareTo(BigDecimal.ZERO)>0 && newPriceDiff.compareTo(BigDecimal.ZERO) < 0){
//                    result = -1;
//                }
//            }
//            priceDiff = newPriceDiff;
//        }
//        return result;
//    }
//
//    /**
//     * 0 无仓， 1买入等待成交， 2 多仓， 3 卖出等待成交
//     * -1异常
//     * @return 策略状态
//     */
//    private int getState(){
//        if(btcBalance.compareTo(initBtcBalance) > 0 ){
//            //多仓
//            if(orderMap.get("buy") != null){
//                logger.Debug("多仓状态有买单，异常退出");
//                System.exit(0);
//            }
//            if(orderMap.get("sell") !=null){
//                return 3;
//            } else{
//                return 2;
//            }
//        } else if (btcBalance.compareTo(initBtcBalance) == 0 ){
//            //无仓
//            if(orderMap.get("sell") != null){
//                logger.Debug("无仓状态有卖单，异常退出");
//                System.exit(0);
//            }
//            if(orderMap.get("buy") !=null){
//                return 1;
//            }else{
//                return 0;
//            }
//        } else {
//            //空仓，本例不做空
//            logger.Debug("btc余额["+btcBalance.toString()+"]小于初始值["+initBtcBalance+"]，异常退出");
//            System.exit(0);
//        }
//        return -1;
//    }

    public static void Array2CSV(List<List<String>> data, String path) {
        try {
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), "UTF-8"));
            for (int i = 0; i < data.size(); i++) {
                List<String> onerow = data.get(i);
                for (int j = 0; j < onerow.size(); j++) {
                    out.write(onerow.get(j));
                    out.write(",");
                }
                out.newLine();
            }
            out.flush();
            out.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public static String getOrderStatusString(OrderStatus status) {
        switch (status) {
            case SendOut:
                return "SendOut";
            case Submitted:
                return "Submitted";
            case AllTraded:
                return "AllTraded";
            case PartlyTraded:
                return "PartlyTraded";
            case Canceled:
                return "Canceled";
            case PartlyCanceled:
                return "PartlyCanceled";
            case TgConnectionError:
                return "TgConnectionError";
            case ExchRejected:
                return "ExchRejected";
            case Error:
                return "Error";
            default:
                return "Error";
        }
    }

    public static OrderStatus getOrderStatusEnum(String status) {
        //System.out.println("getOrderStatusEnum():" + status);

        switch (status) {
            case "sendout":
                return OrderStatus.SendOut;
            case "submitted":
                return OrderStatus.Submitted;
            case "filled":
                return OrderStatus.AllTraded;
            case "partially_filled":
                return OrderStatus.PartlyTraded;
            case "canceled":
                return OrderStatus.Canceled;
            case "partially_canceled":
                return OrderStatus.PartlyCanceled;
            case "tg_connection_error":
                return OrderStatus.TgConnectionError;
            case "exch_rejected":
                return OrderStatus.ExchRejected;
            case "error":
                return OrderStatus.Error;
            default:
                return OrderStatus.Error;
        }
    }

}
